<?php

namespace tmcore\services\pay;

use think\facade\Request;
use tmcore\exceptions\PayException;
use app\common\enum\PayEnum;
use Psr\Http\Message\ResponseInterface;
use Yansongda\Pay\Pay;

/**
 * 支付宝支付服务类
 * @package tmcore\services\pay
 */
class AlipayService extends BasePay
{
    /**
     * 支付配置
     * @var array
     */
    protected array $config;

    /**
     * 支付实例
     * @var Pay
     */

    /**
     * 支付实例
     * @var Pay|\Yansongda\Pay\Provider\Alipay
     */
    protected $app;

    /**
     * 初始化支付宝支付配置
     * @param array $config 支付配置
     * @param array $extend 扩展参数
     */
    public function __construct($config = [], $extend = [])
    {
        if($config) {
            $this->config = $config;
        } else {
            $this->config = $this->getConfig('alipay');
        }

        $pay_config = [
            'alipay' => [
                'default' => [
                    // 必填-支付宝分配的 app_id
                    'app_id' => $this->config['app_id'],
                    // 必填-应用私钥 字符串或路径
                    'app_secret_cert' => $this->config['app_secret_cert'],
                    // 必填-应用公钥证书 路径
                    'app_public_cert_path' => $this->config['app_public_cert_path'],
                    // 必填-支付宝公钥证书 路径
                    'alipay_public_cert_path' => $this->config['alipay_public_cert_path'],
                    // 必填-支付宝根证书 路径
                    'alipay_root_cert_path' => $this->config['alipay_root_cert_path'],
                    'return_url' => $extend['return_url'] ?? Request::domain() . '/http/alipay/return',
                    'notify_url' => $extend['notify_url'] ?? Request::domain() . '/http/alipay/notify',
                    // 选填-第三方应用授权token
                    'app_auth_token' => '',
                    // 选填-服务商模式下的服务商 id，当 mode 为 Pay::MODE_SERVICE 时使用该参数
                    'service_provider_id' => '',
                    // 选填-默认为正常模式。可选为： MODE_NORMAL, MODE_SANDBOX, MODE_SERVICE
                    'mode' => Pay::MODE_NORMAL,
                ],
            ],
            'logger' => [ // optional
                'enable' => false,
                'file' => './runtime/pay/alipay.log',
                'level' => 'info', // 建议生产环境等级调整为 info，开发环境为 debug
                'type' => 'single', // optional, 可选 daily.
                'max_file' => 30, // optional, 当 type 为 daily 时有效，默认 30 天
            ],
            'http' => [ // optional
                'timeout' => 5.0,
                'connect_timeout' => 5.0,
            ],
        ];

        $this->app = Pay::alipay($pay_config);
    }

    /**
     * 支付宝统一下单
     * @param string $pay_for
     * @param array $params ['order_no' => '订单号', 'order_amount' => '订单金额', 'subject' => '订单信息']
     * @return array
     * @throws PayException
     */
    public function pay($pay_for, $params)
    {
        try {
            switch ($pay_for) {
                case 'alipay_web':
                    $result = $this->web($params);
                    break;
                case 'alipay_h5':
                    $result = $this->h5($params);
                    break;
                case 'alipay_scan':
                    $result = $this->scan($params);
                    break;
                case 'alipay_pos':
                    $result = $this->pos($params);
                    break;
                default:
                    throw new \Exception('支付方式错误');
            }

            $this->checkResultFail($result);

            return [
                'data' => $result,
                'pay_type' => PayEnum::ALI_PAY,
                'pay_for' => $pay_for
            ];
        } catch (\Exception $e) {
            throw new PayException($e->getMessage());
        }
    }

    /**
     * 电脑网页支付
     * @param array $params
     * @return array
     */
    public function web($params)
    {
        return $this->app->web([
            'out_trade_no' => $params['order_no'],
            'total_amount' => $params['order_amount'],
            'subject' => $params['subject'],
        ])->toArray();
    }

    /**
     * 手机网站支付
     * @param array $params
     * @return array
     */
    public function h5($params)
    {
        return $this->app->h5([
            'out_trade_no' => $params['order_no'],
            'total_amount' => $params['order_amount'],
            'subject' => $params['subject'],
            'quit_url' => $params['quit_url'] ?? '', // 可用于用户付款中途退出并返回到该参数设置的网站地址上
        ])->toArray();
    }

    /**
     * 扫码支付
     * @param array $params
     * @return array qr_code-扫码url
     */
    public function scan($params)
    {
        return $this->app->scan([
            'out_trade_no' => $params['order_no'],
            'total_amount' => $params['order_amount'],
            'subject' => $params['subject']
        ])->toArray();
    }

    /**
     * 刷卡支付（付款码，被扫码）
     * @param array $params
     * @return array
     */
    public function pos($params)
    {
         return $this->app->pos([
            'out_trade_no' => $params['order_no'],
            'total_amount' => $params['order_amount'],
            'subject' => $params['subject']
        ])->toArray();
    }

    /**
     * 退款
     * @param array $params
     * @return array
     */
    public function refund($params)
    {
        return $this->app->refund([
            'out_trade_no' => $params['order_no'],
            'refund_amount' => $params['refund_amount'],
            'refund_reason' => $params['refund_reason']??'',
        ])->toArray();
    }

    /**
     * 查询订单
     * @param string $order_no 订单号
     * @return array
     */
    public function queryOrder($order_no)
    {
        if(!$order_no) return [];
        return $this->app->query(['out_trade_no' => $order_no])->toArray();
    }

    /**
     * 取消订单
     * @param string $order_no 订单号
     * @return array
     */
    public function cancel($order_no)
    {
        return $this->app->cancel(['out_trade_no' => $order_no])->toArray();
    }

    /**
     * 关闭订单
     * @param string $order_no 订单号
     * @return array
     */
    public function close($order_no)
    {
        return $this->app->close(['out_trade_no' => $order_no])->toArray();
    }

    /**
     * 查询退款订单
     * @param string $order_no 订单号
     * @param string $refund_no 退款单号
     * @return array
     */
    public function queryRefund($order_no, $refund_no)
    {
        if(!$order_no) return [];
        return $this->app->query(['out_trade_no' => $order_no, 'out_request_no' => $refund_no, '_action' => 'refund'])->toArray();
    }

    /**
     * 支付回调
     * @param null $params
     * @return mixed
     */
    public function notify($params = null)
    {
        return $this->app->callback($params);
    }

    /**
     * 确认回调
     * @return ResponseInterface
     */
    public function notifySuccess()
    {
        return $this->app->success();
    }

    /**
     * 捕获错误
     * @param array $result
     * @throws \Exception
     */
    private function checkResultFail($result)
    {
        if (!empty($result['code']) || !empty($result['message'])) {
            throw new \Exception('支付宝支付:'. $result['code'] . '-' . $result['message']);
        }
        if(!(isset($result['code']) && $result['code'] == 10000)) {
            throw new \Exception($result['msg'] . ' ' .$result['sub_msg']);
        }
    }
}